// Licensed under Apache License version 2.0
// Original license LGPL

//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

package com.ctk.wifi.a.bonjour;


import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener;

import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.MulticastLock;
import android.util.Log;

import com.ctk.wifi.a.bonjour.Printer;

/**
 * This class implements a network service listener to discover Printers over a
 * local wifi network, to aid this functionality this class implements the jmdns
 * Service listener which is an implementation of Multi-Cast DNS
 * 
 * @author JPopoka
 */
public class PrinterDiscovery implements ServiceListener {
	public StringBuffer bonjourlist = null;
	
	private final String TAG = this.getClass().getName();
	private Context context;

	/* Service type for printers */
	private static final String SERVICE_TYPE = "_ipp._tcp.local.";

	/* List to store discovered printers */
	private List<Printer> printers;
	private Printer currentPrinter;

	/* Helper used for printer discovery */
	private JmDNS jmdns;

	/* Wifi Lock to keep radio ON as needed */
	private MulticastLock lock;

	/* Singleton Pattern shared instance methods ---------------------------- */
	private static PrinterDiscovery printerDiscovery;

	private PrinterDiscovery(Context context) {
		this.context = context;
		printers = new ArrayList<Printer>();
		bonjourlist = new StringBuffer();
	}

	/**
	 * Singleton accessor to the instance
	 * 
	 * @param context
	 * @return instance - And instance of PrinterDiscovery
	 */
	public static PrinterDiscovery getInstance(Context context) {

		if (printerDiscovery == null)
			printerDiscovery = new PrinterDiscovery(context);
		return printerDiscovery;
	}

	/**
	 * Starts the service to discover printers in the local network
	 */
	public void start() {

		new Thread(new Runnable() {

			@Override
			public void run() {
				startDiscovery();
			}
		}).start();
	}

	private void startDiscovery() {

		WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
		WifiInfo wifiInfo = wifiManager.getConnectionInfo();
		int localIP = wifiInfo.getIpAddress();

		if (localIP != 0) {
			byte[] byteaddr = new byte[] { (byte) (localIP & 0xff), (byte) (localIP >> 8 & 0xff), (byte) (localIP >> 16 & 0xff), (byte) (localIP >> 24 & 0xff) };

			try {
				InetAddress addr = InetAddress.getByAddress(byteaddr);
				InetSocketAddress myServer = new InetSocketAddress(InetAddress.getByAddress(byteaddr), 9100);

				/* Adquire a wifi lock to keep radio ON */
				lock = wifiManager.createMulticastLock("PRT LCK");
				lock.setReferenceCounted(true);
				lock.acquire();

				/* Create an instance of JmDNS and bind it to network */
				jmdns = JmDNS.create(addr, "myJMDNS");

				/* Bind this listener to the service */
				jmdns.addServiceListener(SERVICE_TYPE, this);

				Log.d(TAG, "Listening to Printers");

			} catch (UnknownHostException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	/* Stops the discovery service */
	public synchronized void stop() {
		if (jmdns != null) {
			jmdns.unregisterAllServices();
			try {
				jmdns.close();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				jmdns = null;
			}
		}
		if (lock != null) {
			lock.release();
			lock = null;
		}
		printerDiscovery = null;
	}

	public synchronized List<Printer> getPrinters() {
		return printers;
	}

	public synchronized void selectPrinter(Printer printer) {
		this.currentPrinter = printer;
	}

	public synchronized Printer getCurrentPrinter() {
		return currentPrinter;
	}

	public synchronized Printer getPrinterByName(String name) {
		for (Printer printer : printers) {
			if (printer.getName().equals(name))
				return printer;
		}
		return null;
	}

	/* Service Listener methods --------------------------------------------- */
	// @SuppressWarnings("deprecation")
	@Override
	public void serviceResolved(ServiceEvent ev) {
		bonjourlist.append("service resolved: ").append(ev.getInfo().getName());
		bonjourlist.append("\n");
		bonjourlist.append("port: ").append(ev.getInfo().getPort());
		bonjourlist.append("\n");
		bonjourlist.append("address:").append(ev.getInfo().getInetAddress());
		bonjourlist.append("\n");
		Log.d(TAG, "service resolved: " + ev.getInfo().getName() + ", port: " + ev.getInfo().getPort() + " address: " + ev.getInfo().getInetAddress());
		Printer printer = new Printer(ev.getInfo().getName(), ev.getInfo().getInetAddress());
		printers.add(printer);
	}

	@SuppressWarnings("deprecation")
	@Override
	public void serviceRemoved(ServiceEvent ev) {
		bonjourlist.append("service removed: ").append(ev.getInfo().getName());
		bonjourlist.append("\n");
		bonjourlist.append("port: ").append(ev.getInfo().getPort());
		bonjourlist.append("\n");
		bonjourlist.append("address:").append(ev.getInfo().getInetAddress());
		bonjourlist.append("\n");
		Log.d(TAG, "service removed: " + ev.getName());
		Printer printer = new Printer(ev.getInfo().getName(), ev.getInfo().getInetAddress());
		printers.remove(printer);
	}

	@Override
	public void serviceAdded(ServiceEvent ev) {
		bonjourlist.append("service added: ").append(ev.getInfo().getName());
		bonjourlist.append("\n");
		bonjourlist.append("port: ").append(ev.getInfo().getPort());
		bonjourlist.append("\n");
		bonjourlist.append("address:").append(ev.getInfo().getInetAddress());
		bonjourlist.append("\n");
		Log.d(TAG, "service added, name: " + ev.getName() + ", type: " + ev.getType());
		jmdns.requestServiceInfo(ev.getType(), ev.getName(), 1);
	}
}